
package com.devframe.sys.util.cglib;
/**
 * 版权所有：福建邮科电信业务部厦门研发中心 
 *====================================================
 * 文件名称: MyBeanCopier.java
 * 修订记录：
 * No    日期				作者(操作:具体内容)
 * 1.    2013-1-12			詹保山（softzbs@126.com）(创建:创建文件)
 *====================================================
 * 类描述：(说明未实现或其它不应生成javadoc的内容)
 * 
 */
import java.beans.PropertyDescriptor;
import java.lang.reflect.*;

import net.sf.cglib.core.*;

import org.apache.commons.lang.StringUtils;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Type;
import org.springframework.beans.BeanUtils;


import java.sql.Timestamp;
import java.util.*;

abstract public class MyBeanCopier
{
    private static final BeanCopierKey KEY_FACTORY =(BeanCopierKey)KeyFactory.create(BeanCopierKey.class);
    private static final Type CONVERTER =TypeUtils.parseType("net.sf.cglib.core.Converter");
    private static final Type BEAN_COPIER =TypeUtils.parseType(MyBeanCopier.class.getName());
    private static final Signature COPY =
      new Signature("copy", Type.VOID_TYPE, new Type[]{ Constants.TYPE_OBJECT, Constants.TYPE_OBJECT, CONVERTER });
    private static final Signature CONVERT =TypeUtils.parseSignature("Object convert(Object, Class, Object)");
    
    interface BeanCopierKey {
        public Object newInstance(String source, String target, boolean useConverter);
    }
    public static MyBeanCopier create(Class source, Class target, boolean useConverter) {
        Generator gen = new Generator();
        gen.setSource(source);
        gen.setTarget(target);
        gen.setUseConverter(useConverter);
        return gen.create();
    }
    /**
     * 存放所有BEAN的get  method方法
     */
    private static  Map<String,Map<String,Method>> BeanFieldGetMethod=new HashMap<String,Map<String,Method>>();
    

    abstract public void copy(Object from, Object to, Converter converter);

    public static class Generator extends AbstractClassGenerator {
        private static final Source SOURCE = new Source(MyBeanCopier.class.getName());
        private Class source;
        private Class target;
        private boolean useConverter;
        

        public Generator() {
            super(SOURCE);
        }

        public void setSource(Class source) {
            if(!Modifier.isPublic(source.getModifiers())){ 
               setNamePrefix(source.getName());
            }
            this.source = source;
        }
        
        public void setTarget(Class target) {
            if(!Modifier.isPublic(target.getModifiers())){ 
               setNamePrefix(target.getName());
            }
          
            this.target = target;
        }

        public void setUseConverter(boolean useConverter) {
            this.useConverter = useConverter;
        }

        protected ClassLoader getDefaultClassLoader() {
            return source.getClassLoader();
        }

        public MyBeanCopier create() {
            Object key = KEY_FACTORY.newInstance(source.getName(), target.getName(), useConverter);
            return (MyBeanCopier)super.create(key);
        }
        
		private boolean isSimpleType(Class clazz) {// zhanbs add
			if (clazz.isPrimitive() || clazz.equals(Date.class)
					|| clazz.equals(Boolean.class)|| clazz.equals(Short.class)
					|| clazz.equals(Integer.class) || clazz.equals(Long.class)
					|| clazz.equals(Double.class) || clazz.equals(Float.class)
					|| clazz.equals(Byte.class)
					|| clazz.equals(Character.class)
					|| clazz.equals(java.sql.Date.class)
					|| clazz.equals(String.class)
					|| clazz.equals(Timestamp.class)) {
				return true;
			}
			return false;
		}

        public void generateClass(ClassVisitor v) {
            Type sourceType = Type.getType(source);
            Type targetType = Type.getType(target);
            ClassEmitter ce = new ClassEmitter(v);
            ce.begin_class(Constants.V1_2,
                           Constants.ACC_PUBLIC,
                           getClassName(),
                           BEAN_COPIER,
                           null,
                           Constants.SOURCE_FILE);

            EmitUtils.null_constructor(ce);
            CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, COPY, null);
            PropertyDescriptor[] getters = ReflectUtils.getBeanGetters(source);
            PropertyDescriptor[] setters = ReflectUtils.getBeanGetters(target);

            Map names = new HashMap();
            for (int i = 0; i < getters.length; i++) {
                names.put(getters[i].getName(), getters[i]);
            }
            Local targetLocal = e.make_local();
            Local sourceLocal = e.make_local();
            if (useConverter) {
                e.load_arg(1);
                e.checkcast(targetType);
                e.store_local(targetLocal);
                e.load_arg(0);                
                e.checkcast(sourceType);
                e.store_local(sourceLocal);
            } else {
                e.load_arg(1);
                e.checkcast(targetType);
                e.load_arg(0);
                e.checkcast(sourceType);
            }
            Method getMethod;
            Method setMethod;
            
            String clazzName=target.getName();
            Map<String,Method> classGetMethodMap=new HashMap<String,Method>();
            BeanFieldGetMethod.put(clazzName,classGetMethodMap);
            
            for (int i = 0; i < setters.length; i++) {
                PropertyDescriptor setter = setters[i];
                PropertyDescriptor getter = (PropertyDescriptor)names.get(setter.getName());
                if (getter != null) {
                	//zhanbs update
                	getMethod=getter.getReadMethod();
            
                	setMethod=setter.getWriteMethod();
                 
                	if(getMethod!=null && setMethod!=null  && this.isSimpleType(getMethod.getReturnType())){
                        MethodInfo read = ReflectUtils.getMethodInfo(getMethod);
                        MethodInfo write = ReflectUtils.getMethodInfo(setMethod);
                        //属性的访问方法
                        classGetMethodMap.put((setter.getName()).toUpperCase(), getMethod);
                        
                        if (useConverter) {
                            Type setterType = write.getSignature().getArgumentTypes()[0];
                            e.load_local(targetLocal);
                            e.load_arg(2);
                            e.load_local(sourceLocal);
                            e.invoke(read);
                            e.box(read.getSignature().getReturnType());
                            EmitUtils.load_class(e, setterType);
                            e.push(write.getSignature().getName());
                            e.invoke_interface(CONVERTER, CONVERT);
                            e.unbox_or_zero(setterType);
                            e.invoke(write);
                        } else if (compatible(getter, setter)) {
                            e.dup2();
                            e.invoke(read);
                            e.invoke(write);
                        }
                	}
       
                }
            }
            e.return_value();
            e.end_method();
            ce.end_class();
        }

        private static boolean compatible(PropertyDescriptor getter, PropertyDescriptor setter) {
            return setter.getPropertyType().isAssignableFrom(getter.getPropertyType());
        }

        protected Object firstInstance(Class type) {
            return ReflectUtils.newInstance(type);
        }

        protected Object nextInstance(Object instance) {
            return instance;
        }
    }
    
	public  Map<String,Method> accessBeanFiledGetMethod(String className) {
		return BeanFieldGetMethod.get(className);
	}
	
}
